﻿namespace AzureLogProviders.Providers.EnterpriseLibrary
{
    using System;
    using System.Collections.Generic;
    using System.Configuration.Provider;
    using System.Diagnostics;

    using AzureLogProviders.Common;

    using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
    using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Fluent;
    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
    using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
    using Microsoft.WindowsAzure.ServiceRuntime;
    using System.IO;

    public class EnterpriseRollingFileLogProvider : EnterpriseLogProviderBase
    {
        #region Fields

        //private static readonly ConfigurationSourceBuilder Builder = new ConfigurationSourceBuilder();

        private string fileExistsBehaviour;
        private string formatterType;
        private string maxFile;
        private string messageFooter;
        private string messageHeader;
        private string rollInterval;
        private string rollSizeKB;
        private string template;
        private string timestampPattern;
        private string traceOutputOptions;

        #endregion Fields

        #region Properties

        public String FileName
        {
            get;
            private set;
        }

        public LogFormatter Formatter
        {
            get;
            private set;
        }

        public LogFormatterType LogFormatterType
        {
            get;
            private set;
        }

        public LogSeverityLevel SeverityLevel
        {
            get;
            private set;
        }

        public bool TracingEnabled
        {
            get;
            private set;
        }

        #endregion Properties

        #region Methods

        public override void EndLogicalGrouping(Guid guid)
        {
            Utilities.StopLogicalOperation();
            Utilities.SetActivityId(guid);
        }

        public override Guid GetLogicalGroupId()
        {
            return Utilities.GetActivityId();
        }

        public override LogSeverityLevel GetLogLevel()
        {
            return this.SeverityLevel;
        }

        public override ITracer GetTracer()
        {
            return new EnterpriseTracer(this.TracingEnabled, this);
        }

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);

            this.FileName = config[Constants.ConfigKeys.FileName] ?? string.Empty;

            if (string.IsNullOrWhiteSpace(this.FileName))
            {
                throw new ProviderException(Constants.ExceptionMessages.InvalidFileName);
            }

            string severityFilter = config[Constants.ConfigKeys.SeverityFilter] ?? string.Empty;

            if (!string.IsNullOrWhiteSpace(severityFilter))
            {
                this.SeverityLevel = (LogSeverityLevel)Enum.Parse(typeof(LogSeverityLevel), severityFilter);
            }
            else
            {
                throw new ProviderException(Constants.ExceptionMessages.InvalidSeverityLevel);
            }

            this.maxFile = config[Constants.ConfigKeys.MaxArchivedFile] ?? string.Empty;
            this.messageFooter = config[Constants.ConfigKeys.MessageFooter] ?? string.Empty;
            this.messageHeader = config[Constants.ConfigKeys.MessageHeader] ?? string.Empty;
            this.rollInterval = config[Constants.ConfigKeys.RollInterval] ?? string.Empty;
            this.rollSizeKB = config[Constants.ConfigKeys.RollSizeKB] ?? string.Empty;
            this.fileExistsBehaviour = config[Constants.ConfigKeys.FileExistsBehaviour] ?? string.Empty;
            this.timestampPattern = config[Constants.ConfigKeys.TimestampPattern] ?? string.Empty;
            this.traceOutputOptions = config[Constants.ConfigKeys.TraceOutputOptions] ?? string.Empty;

            string tracingEnabled = config[Constants.ConfigKeys.TracingEnabled] ?? string.Empty;

            this.TracingEnabled = !string.IsNullOrWhiteSpace(tracingEnabled) && Convert.ToBoolean(tracingEnabled);

            this.formatterType = config[Constants.ConfigKeys.FormatterType] ?? string.Empty;

            if (string.IsNullOrWhiteSpace(this.formatterType))
            {
                this.LogFormatterType = LogFormatterType.Text;
            }
            else
            {
                this.LogFormatterType = (LogFormatterType)Enum.Parse(typeof(LogFormatterType), this.formatterType);
            }

            if (this.LogFormatterType == LogFormatterType.Binary)
            {
                this.Formatter = new BinaryLogFormatter();
            }
            else
            {
                this.template = config[Constants.ConfigKeys.Template] ?? string.Empty;
                this.Formatter = string.IsNullOrWhiteSpace(this.template) ? new TextFormatter(this.template) : new TextFormatter(Constants.Others.DefaultTemplate);
            }

               this.SetupEnterpriseConfiguration(false);
        }

        public override void SetSeverityLevel(LogSeverityLevel severityLevel)
        {
            this.SeverityLevel = severityLevel;
            int i = 0;
            foreach (var provider in LoggerFactory.LogProviders.Providers)
            {
                EnterpriseRollingFileLogProvider entProvider = provider as EnterpriseRollingFileLogProvider;
                if (entProvider != null)
                {
                    entProvider.SetupEnterpriseConfiguration(i++ == 0);
                }
            }
        }

        public override void SetupEnterpriseConfiguration(bool overrideSettings)
        {
            base.SetupEnterpriseConfiguration(overrideSettings);


            string logFileName = RoleEnvironment.GetLocalResource(Constants.Others.LocalResource).RootPath + @"\" +
                 Path.GetFileNameWithoutExtension(this.FileName) + "_" +
                Process.GetCurrentProcess().ProcessName + Path.GetExtension(this.FileName);

            bool alreadyConfigured = false;
            ILoggingConfigurationStart logConfig = builder.ConfigureLogging(overrideSettings, out alreadyConfigured);

            var op = logConfig.WithOptions.LogToCategoryNamed(this.Name).WithOptions.SetAsDefaultCategory();
            op.ToSourceLevels(Utilities.MapSeverityLevelToSourceLevel(this.SeverityLevel));

            var file = op.SendTo.RollingFile(Constants.Others.ListenerName + "_" + this.Name).ToFile(logFileName);

            if (!string.IsNullOrWhiteSpace(this.maxFile))
            {
                file.CleanUpArchivedFilesWhenMoreThan(Convert.ToInt32(this.maxFile));
            }

            if (!string.IsNullOrWhiteSpace(this.messageFooter))
            {
                file.WithFooter(this.messageFooter);
            }

            if (!string.IsNullOrWhiteSpace(this.messageHeader))
            {
                file.WithHeader(this.messageFooter);
            }

            if (!string.IsNullOrWhiteSpace(this.rollInterval))
            {
                file.RollEvery((RollInterval)Enum.Parse(typeof(RollInterval), this.rollInterval));
            }

            if (!string.IsNullOrWhiteSpace(this.rollSizeKB))
            {
                file.RollAfterSize(Convert.ToInt32(this.rollSizeKB));
            }

            if (!string.IsNullOrWhiteSpace(this.fileExistsBehaviour))
            {
                file.WhenRollFileExists((RollFileExistsBehavior)Enum.Parse(typeof(RollFileExistsBehavior), this.fileExistsBehaviour));
            }

            if (!string.IsNullOrWhiteSpace(this.timestampPattern))
            {
                file.UseTimeStampPattern(this.timestampPattern);
            }

            if (!string.IsNullOrWhiteSpace(this.traceOutputOptions))
            {
                file.WithTraceOptions((TraceOptions)Enum.Parse(typeof(TraceOptions), this.traceOutputOptions));
            }

            if (!alreadyConfigured)
            {
                logConfig.WithOptions.DisableTracing();
                file.SpecialSources.LoggingErrorsAndWarningsCategory.WithOptions.ToSourceLevels(SourceLevels.Error)
                    .SpecialSources.AllEventsCategory.WithOptions.ToSourceLevels(SourceLevels.Error).SpecialSources
                    .UnprocessedCategory.WithOptions.ToSourceLevels(SourceLevels.Error);
            }

            var configSource = new DictionaryConfigurationSource();
            builder.UpdateConfigurationWithReplace(configSource);
            EnterpriseLibraryContainer.Current
              = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);
        }

        public override Guid StartLogicalGrouping(string groupName)
        {
            Utilities.StartLogicalOperation(groupName);
            Guid prevGuid = Utilities.GetActivityId();
            Utilities.SetActivityId(Guid.NewGuid());
            return prevGuid;
        }

        public override void Write(object message, LogSeverityLevel severityLevel)
        {
            EnterpriseLogEntry entry = new EnterpriseLogEntry(this.Formatter) { Message = message.ToString() };
            entry.Categories.Clear();
            entry.Categories.Add(this.Name);
            if (Utilities.GetActivityId() != Guid.Empty && !entry.ExtendedProperties.ContainsKey(Constants.Others.ActivityId))
            {
                entry.ExtendedProperties.Add(Constants.Others.ActivityId, Utilities.GetActivityId());
            }

            entry.Severity = Utilities.MapSeverityLevelToEventLogType(severityLevel);
            Logger.Write(entry);
        }

        public override void Write(object message, LogSeverityLevel severityLevel, Exception exception)
        {
            EnterpriseLogEntry entry = new EnterpriseLogEntry(this.Formatter) { Message = message.ToString() };
            entry.Categories.Clear();
            entry.Categories.Add(this.Name);
            entry.Severity = Utilities.MapSeverityLevelToEventLogType(severityLevel);
            if (Utilities.GetActivityId() != Guid.Empty && !entry.ExtendedProperties.ContainsKey(Constants.Others.ActivityId))
            {
                entry.ExtendedProperties.Add(Constants.Others.ActivityId, Utilities.GetActivityId());
            }

            if (exception != null)
            {
                entry.ExtendedProperties.Add(Constants.Others.Exception, exception);
            }

            Logger.Write(entry);
        }

        public override void Write(object message, LogSeverityLevel severityLevel, Exception exception, string originator)
        {
            EnterpriseLogEntry entry = new EnterpriseLogEntry(this.Formatter) { Message = message.ToString() };
            entry.Categories.Clear();
            entry.Categories.Add(this.Name);
            entry.Severity = Utilities.MapSeverityLevelToEventLogType(severityLevel);
            if (Utilities.GetActivityId() != Guid.Empty && !entry.ExtendedProperties.ContainsKey(Constants.Others.ActivityId))
            {
                entry.ExtendedProperties.Add(Constants.Others.ActivityId, Utilities.GetActivityId());
            }

            if (exception != null)
            {
                entry.ExtendedProperties.Add(Constants.Others.Exception, exception);
            }

            if (!string.IsNullOrEmpty(originator))
            {
                entry.ExtendedProperties.Add(Constants.Others.Originator, originator);
            }

            Logger.Write(entry);
        }

        public override void Write(object message, LogSeverityLevel severityLevel, Exception exception, string originator, IDictionary<string, object> properties)
        {
            EnterpriseLogEntry entry = new EnterpriseLogEntry(this.Formatter) { Message = message.ToString() };
            entry.Categories.Clear();
            entry.Categories.Add(this.Name);
            entry.Severity = Utilities.MapSeverityLevelToEventLogType(severityLevel);
            entry.ExtendedProperties = new Dictionary<string, object>(properties);
            if (Utilities.GetActivityId() != Guid.Empty && !entry.ExtendedProperties.ContainsKey(Constants.Others.ActivityId))
            {
                entry.ExtendedProperties.Add(Constants.Others.ActivityId, Utilities.GetActivityId());
            }

            if (exception != null)
            {
                entry.ExtendedProperties.Add(Constants.Others.Exception, exception);
            }

            if (!string.IsNullOrEmpty(originator))
            {
                entry.ExtendedProperties.Add(Constants.Others.Originator, originator);
            }

            Logger.Write(entry);
        }

        #endregion Methods
    }
}